package cn.tedu.csmall.product.webapi.service;

import cn.tedu.csmall.common.ex.ServiceException;
import cn.tedu.csmall.common.web.State;
import cn.tedu.csmall.pojo.dto.CategoryBaseSimpleDTO;
import cn.tedu.csmall.pojo.entity.Category;
import cn.tedu.csmall.pojo.dto.CategoryAddNewDTO;
import cn.tedu.csmall.pojo.vo.CategoryBaseSimpleVO;
import cn.tedu.csmall.pojo.vo.CategoryDetailsVO;
import cn.tedu.csmall.pojo.vo.CategorySimpleListItemVO;
import cn.tedu.csmall.pojo.vo.CategorySimpleVO;
import cn.tedu.csmall.product.service.ICategoryService;
import cn.tedu.csmall.product.webapi.mapper.CategoryMapper;
import cn.tedu.csmall.product.webapi.repository.ICategoryRedisRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.sql.Connection;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author xiayiyou
 * @date 2022/6/10 11:42
 */
@Slf4j
@Service
public class CategoryServiceImpl implements ICategoryService {

    @Autowired
    CategoryMapper categoryMapper;

    @Autowired
    private ICategoryRedisRepository categoryRedisRepository;


    @Override
    public void addNew(CategoryAddNewDTO categoryAddNewDTO) {
        // 从参数中取出尝试添加的类别的名称
        String name=categoryAddNewDTO.getName();
        // 调用categoryMapper.getByName()方法查询
        CategorySimpleVO queryResult=categoryMapper.getByName(name);
        // 判断查询结果是否不为null
        if (queryResult!=null){
            // 是：抛出ServiceException
            throw new ServiceException(State.ERR_CATEGORY_NAME_DUPLICATE,
                    "添加类别失败，名称（" + name + "）已存在！");
        }
        // 从参数中取出父级类别的id：parentId
        Long parentId=categoryAddNewDTO.getParentId();
        // 判断parentId是否为0,默认parentId为1;
        Integer depth = 1;
        CategorySimpleVO parentCategory = null;
        if (parentId!=0){
            // 否：此次尝试添加的不是一级类别，则应该存在父级类别，调用categoryMapper.getById()方法查询父级类别的信息
            parentCategory=categoryMapper.getById(parentId);

            // -- 判断查询结果是否为null
            if (parentCategory==null){
                // -- 是：抛出ServiceException
                throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,
                        "添加类别失败，父级类别不存在！");
            }
            // -- 否：当前depth >>> 父级depth + 1
            depth = parentCategory.getDepth()+1;
        }

        // 创建Category对象
        Category category =new Category();
        // 调用BeanUtils.copyProperties()将参数对象中的属性值复制到Category对象中
        BeanUtils.copyProperties(categoryAddNewDTO,category);
        // 补全Category对象中的属性值：depth >>> 前序运算结果
        category.setDepth(depth);
        // 补全Category对象中的属性值：enable >>> 1（默认即启用）
        category.setEnable(1);
        // 补全Category对象中的属性值：isParent >>> 0
        category.setIsParent(0);
        // 补全Category对象中的属性值：gmtCreate, gmtModified >>> LocalDateTime.now()
        LocalDateTime now= LocalDateTime.now();
        category.setGmtCreate(now);
        category.setGmtModified(now);
        // 调用categoryMapper.insert(Category)插入类别数据，获取返回的受影响的行数
        int rows = categoryMapper.insert(category);
        // 判断返回的受影响的行数是否不为1
        if (rows != 1){
            // 是：抛出ServiceException
            throw new ServiceException(State.ERR_INSERT,
                    "添加类别失败，服务器忙（" + State.ERR_INSERT.getValue() + "），请稍后再次尝试！");
        }

        // 判断父级类别的isParent是否为0
        if (parentId !=0 && parentCategory !=null && parentCategory.getIsParent() ==0){
            // 是：调用categoryMapper.updateIsParentById()方法，将父级类别的isParent修改为1，获取返回的受影响的行数
            rows =categoryMapper.updateIsParentById(parentId,1);
            // 判断返回的受影响的行数是否不为1
            if (rows != 1){
                // 是：抛出ServiceException
                throw new ServiceException(State.ERR_UPDATE,
                        "添加类别失败，服务器忙（" + State.ERR_UPDATE.getValue() + "），请稍后再次尝试！");
            }
        }
    }

    @Override
    public CategorySimpleVO getById(Long id){
        return categoryMapper.getById(id);
    }

    @Override
    public List<CategorySimpleListItemVO> listByParentId(Long parentId) {
        return categoryMapper.listByParentId(parentId);

    }

    @Override
    public CategoryDetailsVO getDetailsById(Long id) {
        /*CategoryDetailsVO category=categoryMapper.getDetailsById(id);
        if (category ==null){
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"获取类别详情失败，尝试访问的数据不存在");
        }
        return category;*/

// ===== 以下是新的业务，将从Redis中获取数据 =====
        log.debug("根据id（{}）获取类别详情……", id);
        // 从repository中调用方法，根据id获取缓存的数据

        // 判断缓存中是否存在与此id对应的key
        boolean exists = categoryRedisRepository.exists(id);
        if (exists) {
            // -- 判断此key对应的数据是否为null
            CategoryDetailsVO cacheResult = categoryRedisRepository.getDetailsById(id);
            if (cacheResult == null) {
                // -- 是：表示明确的存入了null值，则此id对应的数据确实不存在，则抛出异常
                log.warn("在缓存中存在此id（）对应的Key，却是null值，则抛出异常", id);
                throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,
                        "获取类别详情失败，尝试访问的数据不存在！");
            } else {
                // -- 否：表示明确的存入了有效数据，则返回此数据即可
                return cacheResult;
            }
        }


        // 缓存中没有此id匹配的数据
        // 从mapper中调用方法，根据id获取数据库的数据
        log.debug("没有命中缓存，则从数据库查询数据……");
        CategoryDetailsVO dbResult = categoryMapper.getDetailsById(id);
        // 判断从数据库中获取的结果是否为null
        if (dbResult == null) {
            // 是：数据库也没有此数据，先向缓存中写入错误数据，再抛出异常
            log.warn("数据库中也无此数据（id={}），先向缓存中写入错误数据", id);
            categoryRedisRepository.saveEmptyValue(id);
            log.warn("抛出异常");
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,
                    "获取类别详情失败，尝试访问的数据不存在！");
        }

        // 将从数据库中查询到的结果存入到缓存中
        log.debug("已经从数据库查询到匹配的数据，将数据存入缓存……");
        categoryRedisRepository.save(dbResult);
        // 返回查询结果
        log.debug("返回查询到数据：{}", dbResult);
        return dbResult;




    }

    @Override
    public void preloadCache() {
        log.debug("删除缓存中的类别列表……");
        categoryRedisRepository.deleteList();
        log.debug("删除缓存中的各独立的类别数据……");
        categoryRedisRepository.deleteAllItem();

        log.debug("从数据库查询类别列表……");
        List<CategoryDetailsVO> list = categoryMapper.list();
        for (CategoryDetailsVO category : list) {
            log.debug("查询结果：{}", category);
            log.debug("将当前类别存入到Redis：{}", category);
            categoryRedisRepository.save(category);
        }

        log.debug("将类别列表写入到Redis……");
        categoryRedisRepository.save(list);
        log.debug("将类别列表写入到Redis完成！");
    }

    /**
     * 根据类别的id，禁用对应的类别，- 类别不存在时抛出异常- 类别已禁用时抛出异常
     * */
    @Override
    public void disableById(Long id){
        //根据id查询是否存在类
        CategoryDetailsVO category= categoryMapper.getDetailsById(id);
        log.debug("根据id查询类别的结果为："+category);
        //如果查询结果为null,则抛出异常
        if (category==null){
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"该类别不存在,请输入正确的id");
        }
        //查询到了结果，如果类别已禁用，则抛出异常
        if(category.getEnable()==0){
            throw new ServiceException(State.ERR_CATEGORY_DISABLED,"该类别已禁用，不需要重新禁用");
        }
        //下面操作是禁用类别
        categoryMapper.disableById(id);
    }

    /**
     * 根据类别的id，禁用对应的类别，- 类别不存在时抛出异常- 类别已禁用时抛出异常
     * */
    @Override
    public void enableById(Long id){
        //根据id查询是否存在类
        CategoryDetailsVO category= categoryMapper.getDetailsById(id);
        log.debug("根据id查询类别的结果为："+category);
        //如果查询结果为null,则抛出异常
        if (category==null){
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"该类别不存在,请输入正确的id");
        }
        //查询到了结果，如果类别已启用，则抛出异常
        if(category.getEnable()==1){
            throw new ServiceException(State.ERR_CATEGORY_ENABLED,"该类别已启用，不需要重新启用");
        }
        //下面操作是禁用类别
        categoryMapper.enableById(id);
    }

    /**
     *
     *     根据类别的id，隐藏（调整is_display的值）对应的类别（调整is_display的值）
     *     - 类别不存在时抛出异常    - 类别已隐藏时抛出异常
     *
     * */
    @Override
    public void hiddendisplayById(Long id) {
        //根据id查询是否存在类
        CategoryDetailsVO category= categoryMapper.getDetailsById(id);
        log.debug("根据id查询类别的结果为："+category);
        //如果查询结果为null,则抛出异常
        if (category==null){
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"该类别不存在,请输入正确的id");
        }
        //查询到了结果，如果类别已隐藏，则抛出异常
        if(category.getIsDisplay()==0){
            throw new ServiceException(State.ERR_CATEGORY_HIDDEN_DISPLAY,"该类别已隐藏，不需要重新隐藏");
        }
        //下面是隐藏类别功能
        categoryMapper.hiddendisplayById(id);
    }

    /**
     * 根据类别的id，显示（调整is_display的值）对应的类别
     * - 类别不存在时抛出异常
     * - 类别已显示时抛出异常
     * */
    @Override
    public void displayById(Long id) {
        //根据id查询是否存在类
        CategoryDetailsVO category= categoryMapper.getDetailsById(id);
        log.debug("根据id查询类别的结果为："+category);
        //如果查询结果为null,则抛出异常
        if (category==null){
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"该类别不存在,请输入正确的id");
        }
        //查询到了结果，如果类别已显示，则抛出异常
        if(category.getIsDisplay()==1){
            throw new ServiceException(State.ERR_CATEGORY_DISPLAY,"该类别已显示，不需要重新显示");
        }
        //下面是显示类别功能
        categoryMapper.displayById(id);
    }

    /**
     * 根据类别的id，删除对应的类别
     * - 类别不存在时抛出异常
     * - 类别存在子级类别时抛出异常
     * - 如果删除的类别是父级类别中的最后一个类别，删除成功后，父级类别的is_parent需更新为0）
     * */
    @Override
    public void deleteById(Long id) {
        //根据id查询是否存在类
        CategoryDetailsVO category= categoryMapper.getDetailsById(id);
        log.debug("根据id查询类别的结果为："+category);
        //如果查询结果为null,则抛出异常
        if (category==null){
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"该类别不存在,请输入正确的id");
        }

            //根据id(parentid)查询所有子集类别
        List<CategorySimpleListItemVO> list=categoryMapper.listByParentId(id);
        // 类别存在子级类别时抛出异常
        if (!CollectionUtils.isEmpty(list)){
            throw new ServiceException(State.ERR_CATEGORY_FOUND_SUB,"该类别存在子级类别，不能删除");
        }
        //不存在子类别
        //获取父级id，并根据它查询父级类别是否只有一个子类别。
        Long parentId=category.getParentId();
        List<CategorySimpleListItemVO> listByParentId=categoryMapper.listByParentId(parentId);
        // 如果删除的类别是父级类别中的最后一个类别，删除成功后，父级类别的is_parent需更新为0）
        if (listByParentId.size()==1){
            //根据id删除该类别
            categoryMapper.deleteById(id);
            //设置其父类的is_parent为0
            categoryMapper.notIsParent(parentId);
        }
        //父类类别下面不止一个子类，则直接删除该类别。
        categoryMapper.deleteById(id);

    }


    /**
     * 6. 根据类别的id，修改对应的类别的基本信息，包括：名称、关键字、图标、排序序号
     * - 类别不存在时抛出异常
     * - 新名称已经被占用时抛出异常
     * */
    @Override
    public void updateCategoryBaseSimpleById(CategoryBaseSimpleDTO categoryBaseSimpleDTO) {
        //根据id查询是否存在类别
        Long newid=categoryBaseSimpleDTO.getId();
        CategoryBaseSimpleVO categoryBaseSimpleVO=categoryMapper.getCategoryBaseSimpleById(newid);
        //类别不存在时抛出异常
        if (categoryBaseSimpleVO==null){
            throw new ServiceException(State.ERR_CATEGORY_NOT_FOUND,"该类别不存在,请输入正确的id");
        }
        //类别存在
        //获取数据库中的name
        String newname=categoryBaseSimpleDTO.getName();
        CategorySimpleVO categorySimpleVO=categoryMapper.getByName(newname);
        //新名称已经被占用时抛出异常
        if (categorySimpleVO!=null){
            throw new ServiceException(State.ERR_CATEGORY_NAME_DUPLICATE,"新名称已经被占用");
        }
        //将新的基本信息存入数据库
        categoryMapper.updateCategoryBaseSimpleById(categoryBaseSimpleDTO);

    }

    /**
     * 重建缓存
     * */
    @Override
    public void reBuildRedisCategory() {
        //log.debug("删除缓存中的类别列表……");
        categoryRedisRepository.deleteList();
        //log.debug("删除缓存中的各独立的类别数据……");
        categoryRedisRepository.deleteAllItem();

        log.debug("从数据库查询类别列表……");
        List<CategoryDetailsVO> list = categoryMapper.list();

        for (CategoryDetailsVO category : list) {
            log.debug("查询结果：{}", category);
            log.debug("将当前类别存入到Redis：{}", category);
            categoryRedisRepository.save(category);
        }

        log.debug("将类别列表写入到Redis……");
        categoryRedisRepository.save(list);
        log.debug("将类别列表写入到Redis完成！");

    }
}
